package com.xbongbong.saas.service.impl;

import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt;
import com.xbongbong.paas.elasticsearch.pojo.XbbAggregatedPage;
import com.xbongbong.paas.elasticsearch.util.EsUtil;
import com.xbongbong.paas.elasticsearch.util.XbbElasticsearchRestTemplate;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.enums.IndexTypeEnum;
import com.xbongbong.paas.pojo.ProductFieldPojo;
import com.xbongbong.paas.pojo.dto.ProductMultiUnitAddDTO;
import com.xbongbong.paas.pojo.dto.ProductMultiUnitDetailDTO;
import com.xbongbong.paas.pojo.dto.ProductMultiUnitListDTO;
import com.xbongbong.paas.pojo.dto.ProductMultiUnitSortUpdateDTO;
import com.xbongbong.paas.pojo.dto.ProductMultiUnitUpdateDTO;
import com.xbongbong.paas.pojo.vo.ProductMultiUnitDetailVO;
import com.xbongbong.paas.pojo.vo.ProductMultiUnitListVO;
import com.xbongbong.paas.redis.PaasRedisHelper;
import com.xbongbong.paas.redis.RedisPrefixConstant;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.DateTimeUtil;
import com.xbongbong.paas.toolbox.util.StringUtil;
import com.xbongbong.paas.toolbox.wrap.BaseVO;
import com.xbongbong.pro.constant.ElasticConstant;
import com.xbongbong.pro.enums.errorcodes.ProductErrorCodeEnum;
import com.xbongbong.saas.constant.ParameterConstant;
import com.xbongbong.saas.constant.StringConstant;
import com.xbongbong.saas.domain.entity.ProductUnitGroupEntity;
import com.xbongbong.saas.domain.entity.ProductUnitRateEntity;
import com.xbongbong.saas.enums.business.ProductEnum;
import com.xbongbong.saas.help.SaasFormHelp;
import com.xbongbong.saas.model.ProductUnitGroupModel;
import com.xbongbong.saas.model.ProductUnitRateModel;
import com.xbongbong.saas.service.ProductMultiUnitService;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;

@Service("productMultiUnitService")
public class ProductMultiUnitServiceImpl implements ProductMultiUnitService {

    @Resource
    private ProductUnitGroupModel productUnitGroupModel;
    @Resource
    private ProductUnitRateModel productUnitRateModel;
    @Resource
    private XbbElasticsearchRestTemplate xbbElasticsearchRestTemplate;
    @Resource
    private PaasRedisHelper paasRedisHelper;
    @Resource
    private SaasFormHelp saasFormHelp;

    @Override
    public ProductMultiUnitListVO list(ProductMultiUnitListDTO productMultiUnitListDTO) throws XbbException {
        ProductMultiUnitListVO productMultiUnitListVO = new ProductMultiUnitListVO();
        String corpid = productMultiUnitListDTO.getCorpid();
        Map<String, Object> params = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        params.put(ParameterConstant.CORPID, corpid);
        params.put(ParameterConstant.DEL, DelEnum.NORMAL.getDel());
        params.put(ParameterConstant.COLUMNS, " id, name, sort ");
        params.put("orderByStr", " sort DESC ");
        List<ProductUnitGroupEntity> productUnitGroupEntityList = productUnitGroupModel.findEntitys(params);
        productMultiUnitListVO.setList(productUnitGroupEntityList);
        ProductFieldPojo stockFieldPojo = saasFormHelp.getProductFieldPojo(RedisPrefixConstant.PRODUCT_STOCK,ProductEnum.STOCK.getAttr(), corpid);
        productMultiUnitListVO.setAccuracy(stockFieldPojo.getAccuracy());
        return productMultiUnitListVO;
    }

    @Override
    public ProductMultiUnitDetailVO detail(ProductMultiUnitDetailDTO productMultiUnitDetailDTO) throws XbbException {
        ProductMultiUnitDetailVO productMultiUnitDetailVO = new ProductMultiUnitDetailVO();
        String corpid = productMultiUnitDetailDTO.getCorpid();
        Long groupId = productMultiUnitDetailDTO.getGroupId();
        Map<String, Object> params = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        params.put(ParameterConstant.CORPID, corpid);
        params.put(ParameterConstant.DEL, DelEnum.NORMAL.getDel());
        params.put("orderByStr", " sort ASC ");
        params.put("groupId", groupId);
        params.put(ParameterConstant.COLUMNS, " id, name, rate, is_base, sort ");
        List<ProductUnitRateEntity> productUnitRateEntityList = productUnitRateModel.findEntitys(params);
        Iterator<ProductUnitRateEntity> iterator = productUnitRateEntityList.iterator();
        while (iterator.hasNext()) {
            ProductUnitRateEntity productUnitRateEntity = iterator.next();
            if (Objects.equals(productUnitRateEntity.getIsBase(), 1)) {
                productMultiUnitDetailVO.setBaseUnitId(productUnitRateEntity.getId());
                productMultiUnitDetailVO.setBaseUnitName(productUnitRateEntity.getName());
                iterator.remove();
                break;
            }
        }
        productMultiUnitDetailVO.setProductUnitRateList(productUnitRateEntityList);
        productMultiUnitDetailVO.setGroupId(groupId);
        boolean isUse = isUse(corpid, groupId);
        productMultiUnitDetailVO.setEditable(isUse ? 0 : 1);
        return productMultiUnitDetailVO;
    }

    @Override
    public BaseVO add(ProductMultiUnitAddDTO productMultiUnitAddDTO) throws XbbException {
        String corpid = productMultiUnitAddDTO.getCorpid();
        String baseName = productMultiUnitAddDTO.getBaseUnitName();
        List<ProductUnitRateEntity> productUnitRateList = productMultiUnitAddDTO.getProductUnitRateList();
        // 辅助单位不超过10个
        if (productUnitRateList.size() > 10 || productUnitRateList.size() == 0) {
            throw new XbbException(ProductErrorCodeEnum.API_ERROR_205105);
        }
        if (StringUtil.isEmpty(baseName)) {
            throw new XbbException(ProductErrorCodeEnum.API_ERROR_205109);
        }
        Map<String, Object> params = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        params.put(ParameterConstant.CORPID, corpid);
        params.put(ParameterConstant.DEL, DelEnum.NORMAL.getDel());
        params.put(ParameterConstant.COLUMNS, "name, sort ");
        params.put("orderByStr", " sort DESC ");
        List<ProductUnitGroupEntity> productUnitGroupEntityList = productUnitGroupModel.findEntitys(params);
        // 单位组不超过1000个
        if (productUnitGroupEntityList.size() >= 1000) {
            throw new XbbException(ProductErrorCodeEnum.API_ERROR_205106);
        }
        StringBuilder nameStr = new StringBuilder();
        StringBuilder rateStr = new StringBuilder();
        nameStr.append(baseName);
        rateStr.append(StringConstant.POSITIVE_NUMBER);
        Set<String> nameSet = new HashSet<>();
        nameSet.add(baseName);
        for (ProductUnitRateEntity item : productUnitRateList) {
            String name = item.getName();
            String rate = new BigDecimal(item.getRate().toString()).stripTrailingZeros().toPlainString();
            if (StringUtil.isEmpty(name)) {
                throw new XbbException(ProductErrorCodeEnum.API_ERROR_205109);
            }
            // 同个单位组不允许名称重复
            if (nameSet.contains(name)) {
                throw new XbbException(ProductErrorCodeEnum.API_ERROR_205102);
            }
            nameSet.add(name);
            nameStr.append(StringConstant.COLON).append(name);
            rateStr.append(StringConstant.COLON).append(rate);
        }
        String groupName = nameStr.append(StringConstant.EQUAL).append(rateStr).toString();
        for (ProductUnitGroupEntity item : productUnitGroupEntityList) {
            // 不同单位组名称不允许重复
            if (Objects.equals(groupName, item.getName())) {
                throw new XbbException(ProductErrorCodeEnum.API_ERROR_205103);
            }
        }
        Long now = DateTimeUtil.getInt();
        Integer sort = productUnitGroupEntityList.size() == 0 ? 1 : productUnitGroupEntityList.get(0).getSort() + 1;
        ProductUnitGroupEntity productUnitGroupEntity = new ProductUnitGroupEntity(now, now, DelEnum.NORMAL.getDel());
        productUnitGroupEntity.setCorpid(corpid);
        productUnitGroupEntity.setName(groupName);
        productUnitGroupEntity.setSort(sort);
        productUnitGroupModel.insert(productUnitGroupEntity);
        Long groupId = productUnitGroupEntity.getId();
        int rateSort = 1;
        for (ProductUnitRateEntity item : productUnitRateList) {
            item.setGroupId(groupId);
            item.setCorpid(corpid);
            item.setIsBase(0);
            item.setAddTime(now);
            item.setUpdateTime(now);
            item.setDel(DelEnum.NORMAL.getDel());
            item.setSort(++rateSort);
        }
        ProductUnitRateEntity baseEntity = new ProductUnitRateEntity(corpid, groupId, baseName, 1D, 1, 1, now, now, DelEnum.NORMAL.getDel());
        productUnitRateList.add(0, baseEntity);
        productUnitRateModel.insertBatch(productUnitRateList);
        paasRedisHelper.removeValue(RedisPrefixConstant.PRODUCT_UNIT,corpid);
        return new BaseVO();
    }

    @Override
    public BaseVO update(ProductMultiUnitUpdateDTO productMultiUnitUpdateDTO) throws XbbException {
        // TODO 老数据校验是否有产品在使用，使用则不可变更比例 + 管理更新名称 + 10个辅助单位数量限制
        String corpid = productMultiUnitUpdateDTO.getCorpid();
        Long groupId = productMultiUnitUpdateDTO.getGroupId();
        String baseName = productMultiUnitUpdateDTO.getBaseUnitName();
        List<ProductUnitRateEntity> productUnitRateList = productMultiUnitUpdateDTO.getProductUnitRateList();
        if (productUnitRateList.size() > 10) {
            throw new XbbException(ProductErrorCodeEnum.API_ERROR_205105);
        }
        if (StringUtil.isEmpty(baseName)) {
            throw new XbbException(ProductErrorCodeEnum.API_ERROR_205109);
        }
        boolean isUse = isUse(corpid, groupId);
        Long now = DateTimeUtil.getInt();
        Map<String, Object> params = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        params.put(ParameterConstant.CORPID, corpid);
        params.put(ParameterConstant.DEL, DelEnum.NORMAL.getDel());
        params.put("orderByStr", " sort ASC ");
        params.put("groupId", groupId);
        params.put(ParameterConstant.COLUMNS, " id, name, rate, is_base ");
        List<ProductUnitRateEntity> productUnitRateEntityList = productUnitRateModel.findEntitys(params);
        Map<Long, ProductUnitRateEntity> productUnitRateMap = new HashMap<>(productUnitRateEntityList.size());
        List<ProductUnitRateEntity> updateList = new ArrayList<>();
        // 基础单位名称是否修改
        boolean isRename = false;
        for (ProductUnitRateEntity item : productUnitRateEntityList) {
            if (Objects.equals(item.getIsBase(), 1)) {
                if (!Objects.equals(item.getName(), baseName)) {
                    isRename = true;
                    item.setName(baseName);
                    item.setUpdateTime(now);
                    updateList.add(item);
                }
                continue;
            }
            productUnitRateMap.put(item.getId(), item);
        }
        List<ProductUnitRateEntity> delList = new ArrayList<>();
        List<ProductUnitRateEntity> addList = new ArrayList<>();
        Integer sort = 1;
        StringBuilder nameStr = new StringBuilder();
        StringBuilder rateStr = new StringBuilder();
        nameStr.append(baseName);
        rateStr.append(StringConstant.POSITIVE_NUMBER);
        for (ProductUnitRateEntity item : productUnitRateList) {
            Long id = item.getId();
            Double rate = item.getRate();
            String rateString = new BigDecimal(rate.toString()).stripTrailingZeros().toPlainString();
            String name = item.getName();
            if (StringUtil.isEmpty(name)) {
                throw new XbbException(ProductErrorCodeEnum.API_ERROR_205109);
            }
            nameStr.append(StringConstant.COLON).append(name);
            rateStr.append(StringConstant.COLON).append(rateString);
            if (Objects.nonNull(item.getSort())) {
                sort = item.getSort();
            }
            if (Objects.isNull(id)) {
                addList.add(new ProductUnitRateEntity(corpid, groupId, name, rate, 0, ++sort, now, now, DelEnum.NORMAL.getDel()));
            } else {
                ProductUnitRateEntity databaseEntity = productUnitRateMap.get(id);
                if (isUse) {
                    if (!Objects.equals(databaseEntity.getRate(), rate)) {
                        throw new XbbException(ProductErrorCodeEnum.API_ERROR_205104);
                    } else if (!Objects.equals(databaseEntity.getName(), name)) {
                        ProductUnitRateEntity productUnitRateEntity = new ProductUnitRateEntity();
                        productUnitRateEntity.setId(id);
                        productUnitRateEntity.setName(name);
                        productUnitRateEntity.setUpdateTime(now);
                        productUnitRateEntity.setRate(databaseEntity.getRate());
                        updateList.add(productUnitRateEntity);
                    }
                } else {
                    if (!Objects.equals(databaseEntity.getRate(), rate) || !Objects.equals(databaseEntity.getName(), name)) {
                        updateList.add(new ProductUnitRateEntity(id, name, rate, now));
                    }
                }
                productUnitRateMap.remove(id);
            }
        }
        if (!productUnitRateMap.isEmpty()) {
            if (isUse) {
                throw new XbbException(ProductErrorCodeEnum.API_ERROR_205104);
            }
            delList.addAll(productUnitRateMap.values());
        }
        // 更新组名称
        if (!addList.isEmpty() || !delList.isEmpty() || !updateList.isEmpty() || isRename) {
            String groupName = nameStr.append(StringConstant.EQUAL).append(rateStr).toString();
            params.clear();
            params.put(ParameterConstant.CORPID, corpid);
            params.put(ParameterConstant.DEL, DelEnum.NORMAL.getDel());
            params.put("name", groupName);
            params.put("start", 0);
            params.put("pageSize", 1);
            params.put(ParameterConstant.COLUMNS, " id ");
            boolean isRepeat = productUnitGroupModel.findEntitys(params).size() > 0;
            if (isRepeat) {
                throw new XbbException(ProductErrorCodeEnum.API_ERROR_205103);
            }
            ProductUnitGroupEntity productUnitGroupEntity = new ProductUnitGroupEntity();
            productUnitGroupEntity.setName(groupName);
            productUnitGroupEntity.setCorpid(corpid);
            productUnitGroupEntity.setId(groupId);
            productUnitGroupEntity.setUpdateTime(now);
            productUnitGroupModel.update(productUnitGroupEntity);
            paasRedisHelper.removeValue(RedisPrefixConstant.PRODUCT_UNIT,corpid);
        }
        if (!addList.isEmpty()) {
            productUnitRateModel.insertBatch(addList);
        }
        if (!delList.isEmpty()) {
            productUnitRateModel.deleteBatch(delList, corpid);
        }
        if (!updateList.isEmpty()) {
            productUnitRateModel.updateBatch(updateList, corpid);
        }
        return new BaseVO();
    }

    @Override
    public BaseVO sortUpdate(ProductMultiUnitSortUpdateDTO productMultiUnitSortUpdateDTO) throws XbbException {
        if (productMultiUnitSortUpdateDTO.getList() != null && productMultiUnitSortUpdateDTO.getList().size() > 0) {
            productUnitGroupModel.updateBatch(productMultiUnitSortUpdateDTO.getList(), productMultiUnitSortUpdateDTO.getCorpid());
            paasRedisHelper.removeValue(RedisPrefixConstant.PRODUCT_UNIT,productMultiUnitSortUpdateDTO.getCorpid());
        }
        return new BaseVO();
    }

    @Override
    public BaseVO delete(ProductMultiUnitDetailDTO productMultiUnitDetailDTO) throws XbbException {
        String corpid = productMultiUnitDetailDTO.getCorpid();
        Long groupId = productMultiUnitDetailDTO.getGroupId();
        boolean isUse = isUse(corpid, groupId);
        if (isUse) {
            throw new XbbException(ProductErrorCodeEnum.API_ERROR_205107);
        }
        productUnitGroupModel.deleteByKey(groupId, corpid);
        productUnitRateModel.deleteByGroupId(groupId, corpid);
        paasRedisHelper.removeValue(RedisPrefixConstant.PRODUCT_UNIT,corpid);
        return new BaseVO();
    }

    private boolean isUse (String corpid, Long groupId) throws XbbException {
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery("corpid.keyword",corpid));
        boolQueryBuilder.filter(termQuery(ParameterConstant.DEL,DelEnum.NORMAL.getDel()));
        boolQueryBuilder.filter(termQuery(ProductEnum.getEsAttr4Keyword(ProductEnum.PARENT_ID), 0));
        boolQueryBuilder.filter(termQuery(ProductEnum.getEsAttr4Keyword(ProductEnum.ENABLE_MULTI_UNIT), 1));
        boolQueryBuilder.filter(termQuery(ProductEnum.getEsAttr4Keyword(ProductEnum.UNIT), groupId));
        boolQueryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD,IndexTypeEnum.IDX_SAAS_PRODUCT.getType()));
        sourceBuilder.query(boolQueryBuilder);
        //分页
        PageRequest pageRequest = EsUtil.setPage(sourceBuilder, 1, 1);
        SearchRequest searchRequest = new SearchRequest(IndexTypeEnum.IDX_SAAS_PRODUCT.getIndex());
        searchRequest.source(sourceBuilder);
        XbbAggregatedPage<PaasFormDataEntityExt> esEntities = xbbElasticsearchRestTemplate.queryForPages(pageRequest, searchRequest, PaasFormDataEntityExt.class);
        return esEntities.getTotalElements() > 0;
    }
}
